Created by miccall (转载请注明出处 miccall.tech)
一 . 渲染流程 :
每一层的Shader都维护一个结构体,然后使用inout的形式进行计算,并传入下一流程
首先 Vertex shader 他会创建一个结构体 VertexState 处理 MSET_TESSELLATION 和 Merge 操作
Hull shader 创建 ControlState和PatchState 结构体
Domain shader 创建 ControlState PatchState DomainState 处理 Subdivision 和 Displacement 操作
Geometry shader 创建 GeometryState
Fragment shader 创建 FragmentState ,并处理很多操作,我们的研究也是在这里 ,以上作为了解,不多深究
二. Material 计算步骤
开始向 fragment shader 传入数据 mat/mat.frag :
BEGIN_PARAMS
INPUT0(vec3,fPosition)
INPUT1(vec4,fColor)
INPUT2(vec3,fTangent)
INPUT3(vec3,fBitangent)
INPUT4(vec3,fNormal)
INPUT5(vec4,fTexCoord)
。。。
END_PARAMS
进行相关初始化计算 ,放入 FragmentState 结构体 ,方便以后使用 FragmentState 在 state.frag 中
然后简单看一下结构体里面的相关类型(复制的,忽略大小写 )
VEC3 vertexPosition 在3D空间中的位置。
VEC3 vertexEye 从位置到相机的单位矢量。
flaot vertexEyeDistance 从位置到相机的直接距离。
VEC 2 vertexTexCoord 网格纹理坐标。
VEC 2 vertexTexCoordSecondary 二次网格纹理坐标; 经常缺席/未使用。
vec4 vertexColor 网眼颜色; 如果没有,将是白色的。
VEC3 vertexNormal 网格法向量。
VEC3 vertexTangent 网格切线矢量。
VEC3 vertexBitangent 网格bitangent矢量。
VEC 2 screenTexCoord 坐标为[0,1]中的屏幕位置。可以使用此样本对全屏纹理进行采样。
浮动 screenDepth 投影后深度值。与vertexPosition.z不同。
UINT sampleCoverage 用于样本覆盖的位掩码; 通常仅用于体素传递。
INT instanceID 实例编号; 通常仅用于体素传递。
vec4 albedo rgb中的反照率或漫反射颜色,不透明度可选地为alpha。
VEC3 normal 表面法线方向作为单位矢量。
浮动 gloss 表面粗糙度为[0,1]上的标量。
VEC3 reflectivity 彩色镜面反射率。
VEC3 fresnel 菲涅耳强度。
VEC3 diffuseLight 所有漫射照明的总和。
VEC3 specularLight 所有高光照明的总和。
VEC3 emissiveLight 所有自发光照明的总和。
vec4 generic0 ... 3 一些子程序特殊使用的四个通用值。
vec4 diffuseGI RGB中的全局漫反射照明,A中的掩模。并不总是存在。
vec4 specularGI RGB中的全局镜面反射,A中的掩模。并不总是存在。
vec4 OUTPUT0 ... 7 最终渲染颜色输出。通常只在Merge子例程中修改。
然后就是参数计算,先简单看一下,后面会有源码
Premerge
Surface
Microsurface
Albedo
Reflectivity
Lighting or { Diffusion , Reflection , Occlusion , Cavity }
Emissive
Transparency
Merge
USE_OUTPUT1 2 3 4 5 6 7
三. 灯光 Lighting 项比较特殊
在 Light.frag
的 MaterialLighting( inout FragmentState s ) 方法 :
步骤:
DiffusionEnv
ReflectionEnv
ReflectionSecondaryEnv
Occlusion
循环 uLightCount ,记录 LightParams 参数
other/lightParams.frag
并计算
Diffusion
Reflection
ReflectionSecondary
如果体素渲染要采样
sampleShadowMap
否则
sampleShadowMask
写入结构体参数:
struct LightParams
{
vec3 color; // "colour"
vec3 toSource; // vector from shaded point to light
vec3 direction; // normalized vector to light
float invDistance; // 1/distance to light
float distance; // distance to light
float attenuation; // dimming (distance and other factors)
vec3 attenParams; // attenuation parameters
vec2 spotParams; // spotlight parameters
vec3 size; // width, height, thickness
vec3 axisX, axisY; // 2D area axes
float id; // light index
vec4 shadow; // shadow fraction
};
Cavity
四.一些函数列表
// Premerge
DirectLightPremerge
VoxelizationPremerge
// Surface
SurfaceNormalMap
SurfaceParallaxMap
SurfaceDetailNormalMap
// Microsurface
MicrosurfaceGlossMap
// Albedo
AlbedoMap
AlbedoVertex
AlbedoDota
// Reflectivity
ReflectivityDota
ReflectivityMetalness
ReflectivityRefractiveIndex
ReflectivitySpecularMap
// Lighting
MaterialLighting
// Emissive
EmissiveFluorescentImage
EmssiveHeat
EmssiveMap
DiffusionShadowCatcherEmissive
// Transparency
TransparencyAlphaTest
TransparencyBlend
TransparencyDither
PrepassRefraction
TransparencyRefraction
VoxelAlphaTest
TransparencyRefractionSpecular
// Merge
DirectLightMerge
ExportMerge
LightMerge
PrepassMerge
ScatterMerge
ShadowMapMerge
VoxelizationMerge
VoxelizationMerge
VoxelizationMerge
WireframeMerge
// Diffusion
DiffusionMicrofiberExport
DiffusionScatterExport
DiffusionDotaLight
DiffusionLambertianLight
DiffusionMicrofiberLight
DiffusionShadowCatcherLight
PrepassShadowCatcher
PrepassSubsurface
DiffusionScatterLight
VoxelizeShadowCatcher
// Reflection
ReflectionAniso
ReflectionGGX
ReflectionMirror
ReflectionAnisotropicLight1
ReflectionGGXLight1
MirrorLight1
BlinnPhongLight1
BlinnPhongLight1
五. 源码
下面的部分就是直接抄的了 :
把猴的界面参数:
我们也是按照这个控制,来进行分析:
Subdivision有两个选项 ,第一个是 falt 在 HULL着色器阶段
void PatchFlat( inout ControlState s, inout PatchState ps )
{
ps.edgeTessellation[0] = max( length( s.position[1] - s.position[2] ) * uTessellationFactor, 1.0 );
ps.edgeTessellation[1] = max( length( s.position[2] - s.position[0] ) * uTessellationFactor, 1.0 );
ps.edgeTessellation[2] = max( length( s.position[0] - s.position[1] ) * uTessellationFactor, 1.0 );
ps.centerTessellation = max( max( ps.edgeTessellation[0], ps.edgeTessellation[1]), ps.edgeTessellation[2] );
}
第二个是 PN Triangles 在 domain 阶段 ,处理 Subdivision
void SubdivisionPN( inout ControlState s, inout PatchState ps, inout DomainState ds )
{
vec3 b030 = s.position[0].xyz,
b003 = s.position[1].xyz,
b300 = s.position[2].xyz;
vec3 b201 = ps.constants[0];
vec3 b210 = ps.constants[1];
vec3 b102 = ps.constants[2];
vec3 b111 = ps.constants[3];
vec3 b120 = ps.constants[4];
vec3 b012 = ps.constants[5];
vec3 b021 = ps.constants[6];
float u = s.domainCoord.x,
v = s.domainCoord.y,
w = s.domainCoord.z;
float u2 = u*u, v2 = v*v, w2 = w*w;
float u3 = u2*u, v3 = v2*v, w3 = w2*w;
ds.position = b300*w3 + b030*u3 + b003*v3 +
3.0 * ( b210 * (w2 * u) +
b120 * (w * u2) +
b201 * (w2 * v) +
b021 * (u2 * v) +
b102 * (w * v2) +
b012 * (u * v2) ) +
b111 * ((6.0 * w) * (u * v));
}
displacement 在 domain 阶段
第一个选项是vector
void DisplacementVector( inout ControlState s, inout PatchState ps, inout DomainState ds )
{
vec3 disp = texture2DLod( tDisplacementVectorMap, ds.texcoord.xy, 0.0 ).xyz;
HINT_FLATTEN
if( uDisplacementTangentSpace > 0.0 )
{
disp = disp.x * normalize(ds.tangent) +
disp.y * normalize(ds.bitangent) +
disp.z * normalize(ds.normal);
}
ds.position += uDisplacementScale*disp + uDisplacementBias;
}
第二个选项是 Height
void DisplacementHeight( inout ControlState s, inout PatchState ps, inout DomainState ds )
{
float disp = texture2DLod( tDisplacementHeightMap, ds.texcoord.xy, 0.0 ).x;
disp = disp*uDisplacementScaleBias.x + uDisplacementScaleBias.y;
ds.position += disp * ds.normal;
}
Surface 之后的都在 fragment shader
SurfaceNormalMap
void SurfaceNormalMap( inout FragmentState s )
{
//sample and scale/bias the normal map
vec4 nsamp = textureMaterial( tNormalMap, s.vertexTexCoord );
vec3 n = uNormalMapScale*nsamp.xyz + uNormalMapBias;
//ortho-normalization
vec3 T = s.vertexTangent;
vec3 B = s.vertexBitangent;
vec3 N = s.vertexNormal;
float renormalize = uNormalMapParams.x, orthogonalize = uNormalMapParams.y;
N = mix( N, normalize(N), renormalize );
T -= (orthogonalize * dot(T,N)) * N;
T = mix( T, normalize(T), renormalize );
vec3 orthB = orthogonalize * (dot(B,N)*N + dot(B,T)*T);
// don't subtract if it results in 0, which can't be normalized:
float valueNonZero = float(any(greaterThan( abs(B - orthB), vec3(0.0,0.0,0.0) )));
B -= orthB * valueNonZero;
B = mix( B, normalize(B), renormalize );
//regenerate bitangent
vec3 B2 = cross( N, T );
B2 = dot(B2,B) < 0.0 ? -B2 : B2;
B = mix( B, B2, uNormalMapParams.z );
// object/tangent space switch
HINT_FLATTEN
if( uNormalMapObjectSpace > 0 )
{ n = mulVec( uNormalMapObjectSpaceMatrix, n ); }
else
{ n = n.x*T + n.y*B + n.z*N; }
//store our results
s.normal = normalize( n );
s.vertexTangent = T;
s.vertexBitangent = B;
s.vertexNormal = N;
s.albedo.a = nsamp.a;
}
SurfaceParallaxMap
float ParallaxSample( vec2 c )
{
return 1.0 - dot( texture2DLod( tParallaxHeightMap, c, 0.0 ), uParallaxSwizzle );
}
void SurfaceParallaxMap( inout FragmentState s )
{
vec3 dir = vec3( dot( -s.vertexEye, s.vertexTangent ),
dot( -s.vertexEye, s.vertexBitangent ) * uParallaxFlipY,
dot( -s.vertexEye, s.vertexNormal ) );
vec2 maxOffset = dir.xy * (uParallaxDepthOffset.x / (abs(dir.z) + 0.001));
float minSamples = 16.0;
float maxSamples = 128.0;
float samples = saturate( 3.0*length(maxOffset) );
float incr = rcp( mix( minSamples, maxSamples, samples ) );
vec2 tc0 = s.vertexTexCoord - uParallaxDepthOffset.y*maxOffset;
float h0 = ParallaxSample( tc0 );
HINT_LOOP
for( float i=incr; i<=1.0; i+=incr )
{
vec2 tc = tc0 + maxOffset * i;
float h1 = ParallaxSample( tc );
if( i >= h1 )
{
//hit! now interpolate
float r1 = i, r0 = i-incr;
float t = (h0-r0)/((h0-r0)+(-h1+r1));
float r = (r0-t*r0) + t*r1;
s.vertexTexCoord = tc0 + r*maxOffset;
break;
}
else
{
s.vertexTexCoord = tc0 + maxOffset;
}
h0 = h1;
}
//standard normal mapping
SurfaceNormalMap(s);
}
SurfaceDetailNormalMap
void SurfaceDetailNormalMap( inout FragmentState s )
{
SurfaceNormalMap(s);
//look up detail normal
vec2 uv = lerp( s.vertexTexCoord.xy, s.vertexTexCoordSecondary.xy, uDetailUseSecondaryUV );
uv = uv*uDetailTiling.xy + uDetailTiling.zw;
vec3 dn = textureMaterial( tDetailNormalMap, uv ).xyz;
dn = uDetailNormalMapScale*dn + uDetailNormalMapBias;
//ortho-normalization of new tangent basis
vec3 T = s.vertexTangent;
vec3 B = s.vertexBitangent;
vec3 N = s.normal;
T -= dot(T,N)*N;
T = normalize(T);
B -= dot(B,N)*N + dot(B,T)*T;
B = normalize(B);
//blend in the detail normal
dn = dn.x * T +
dn.y * B +
dn.z * N;
float detailWeight = dot( textureMaterial( tDetailWeightMap, s.vertexTexCoord ), uDetailWeightSwizzle );
detailWeight *= uDetailWeight;
s.normal = normalize( s.normal + dn * detailWeight );
}
Microsurface
gloss
void MicrosurfaceGlossMap( inout FragmentState s )
{
float g = dot( textureMaterial( tGlossMap, s.vertexTexCoord ), uGlossSwizzle );
s.gloss = uGlossScaleBias.x * g + uGlossScaleBias.y;
float h = saturate( dot( s.normal, s.vertexEye ) );
h = uGlossHorizonSmooth - h * uGlossHorizonSmooth;
s.gloss = mix( s.gloss, 1.0, h*h );
}
Albedo
AlbedoMap
void AlbedoMap( inout FragmentState s )
{
s.albedo = textureMaterial( tAlbedoMap, s.vertexTexCoord );
s.albedo.xyz *= uAlbedoMapColor;
}
AlbedoVertex
void AlbedoVertex( inout FragmentState s )
{
AlbedoMap(s);
vec4 vc = s.vertexColor;
//sRGB conversion
vec3 srgb = (vc.xyz*vc.xyz)*(vc.xyz*vec3(0.2848,0.2848,0.2848) + vec3(0.7152,0.7152,0.7152));
vc.xyz = mix( vc.xyz, srgb, uAlbedoVertexColor.z );
//color & alpha enables
s.albedo = mix( s.albedo, s.albedo * vc, uAlbedoVertexColor.xxxy );
}
AlbedoDota
void AlbedoDota( inout FragmentState s )
{
//dota mask 1
vec4 mask1;
mask1.x = dot( textureMaterial( tDotaDetailMask, s.vertexTexCoord ), uDotaDetailChannel );
mask1.y = dot( textureMaterial( tDotaDiffuseFresnelMask, s.vertexTexCoord ), uDotaDiffuseFresnelChannel );
mask1.z = dot( textureMaterial( tDotaMetalnessMask, s.vertexTexCoord ), uDotaMetalnessChannel );
mask1.w = dot( textureMaterial( tDotaSelfIllumMask, s.vertexTexCoord ), uDotaSelfIllumChannel );
//fresnel gradients
float NdotV = saturate( dot( s.normal, s.vertexEye ) );
vec3 fresnel = texture2D( tDotaFresnelWarp, vec2(NdotV,0.0) ).xyz;
//albedo
s.albedo = textureMaterial( tDotaColorMap, s.vertexTexCoord );
//edge color warp
vec3 warpedAlbedo = texture3D( tDotaEdgeColorWarp, s.albedo.xyz ).xyz;
s.albedo.xyz = mix( s.albedo.xyz, warpedAlbedo, fresnel.g * mask1.g );
//detail map (additive only for now, could do other blend modes)
s.albedo.xyz += (uDotaDetail * mask1.r) * textureMaterial( tDotaDetailMap, s.vertexTexCoord * uDotaDetailTile ).xyz;
//self illumination
s.emissiveLight += (uDotaSelfIllumination * mask1.a) * s.albedo.xyz;
//metalness reduces albedo
s.albedo.xyz = s.albedo.xyz - s.albedo.xyz * mask1.b;
//other shading stages will need these values
s.generic0 = vec4( mask1.g, //diffuse gradient selector
mask1.b, //metalness
fresnel.r, //rim light fresnel
fresnel.b ); //specular fresnel
}
Diffusion
DotaLight
void DiffusionDotaLight( inout FragmentState s, LightParams l )
{
adjustAreaLightDiffuse( l, s.vertexPosition );
//dota scales illumination by a user gradient (default is half-lambert)
float NdotL = dot( l.direction, s.normal );
vec3 illum = (1.0/3.1415926) * texture2D( tDotaDiffussionGradient, vec2( 0.5*NdotL + 0.5, s.generic0.x ) ).xyz;
s.diffuseLight += l.attenuation *
(illum * l.shadow.rgb) *
(s.albedo.xyz * l.color);
}
lambert
void DiffusionLambertianLight( inout FragmentState s, LightParams l )
{
adjustAreaLightDiffuse( l, s.vertexPosition );
float lambert = saturate( (1.0/3.1415926) * dot(s.normal, l.direction) );
s.diffuseLight += (lambert * l.attenuation) *
(l.color * l.shadow.rgb) *
s.albedo.xyz;
}
Microfiber
void DiffusionMicrofiberLight( inout FragmentState s, LightParams l )
{
adjustAreaLightDiffuse( l, s.vertexPosition );
float NdotL = dot(s.normal, l.direction);
float lambert = (1.0/3.1415926) * saturate( NdotL );
//PEACH-FUZZ
vec4 fuzzMap = texture2D( tMicrofiberMap, s.vertexTexCoord );
float eyeDP = dot( s.vertexEye, s.normal );
float wrapOcc = 0.5*uMicrofiberScatter;
wrapOcc = wrapLight(NdotL, wrapOcc) * wrapLightIntegral(wrapOcc);
vec3 fuzzLight = diffuseFresnel3( eyeDP, uMicrofiberScatter, l.shadow.rgb, uMicrofiberOcc );
fuzzLight *= wrapOcc;
fuzzLight *= uMicrofiberColor * fuzzMap.rgb;
//occlude by high gloss values, wet cloth is not fuzzy!
float wet = saturate(1.0 - uMaskWithGloss * s.gloss);
fuzzLight *= wet*wet;
s.diffuseLight += (lambert * s.albedo.xyz + fuzzLight) * l.shadow.rgb * l.attenuation * l.color;
}
subsurface Scatter
void DiffusionScatterLight( inout FragmentState s, LightParams l, inout DiffusionParams scatter )
{
adjustAreaLightDiffuse( l, s.vertexPosition );
float DP = dot(l.direction, s.normal);
float lambert = saturate( (1.0/3.1415926) * DP );
//TRANSLUCENCY
float spread = scatter.translucencyScatter * 0.5;
float wrap = wrapLightSquared(-DP, spread) * wrapLightSquaredIntegral(spread);
//3x sets translucency color as a mid-point of the ramp
//0.15 is the bias from pure white at ramp^0
float ex = (-3.0 * l.shadow.a) + 3.15;
vec3 ramp = (0.9975 * scatter.translucencyColor) + vec3(0.0025,0.0025,0.0025);
ramp = pow(ramp*l.shadow.a, ex);
s.diffuseLight +=
(l.attenuation * l.color) *
(lambert * l.shadow.rgb + wrap * ramp * scatter.translucencyMask);
}
Reflectivity
Dota
void ReflectivityDota( inout FragmentState s )
{
//pass through values
#ifdef AlbedoDota_Present
float metalness = s.generic0.y;
float rimFresnel = s.generic0.z;
float reflFresnel = s.generic0.w;
#else
float metalness = 0.0;
float rimFresnel = pow( saturate(1.0 - dot(s.normal,s.vertexEye)), 5.0 );
float reflFresnel = 0.05 + 0.95*rimFresnel;
#endif
#ifdef REFLECTIVITY_DOTA_EXPORT
reflFresnel = 0.08; //bottom of the dota fresnel curve
#endif
//dota mask 2
vec4 mask2;
mask2.x = dot( textureWithSampler( tDotaSpecMask, sDotaSampler, s.vertexTexCoord ), uDotaSpecMaskChannel );
mask2.y = dot( textureWithSampler( tDotaRimMask, sDotaSampler, s.vertexTexCoord ), uDotaRimMaskChannel );
mask2.z = dot( textureWithSampler( tDotaTintMask, sDotaSampler, s.vertexTexCoord ), uDotaTintMaskChannel );
mask2.w = dot( textureWithSampler( tDotaExpMask, sDotaSampler, s.vertexTexCoord ), uDotaExpMaskChannel );
//reflectivity
s.reflectivity = mix( s.albedo.xyz, vec3(1.0,1.0,1.0), mask2.b ); //dota docs have this backwards -jdr & drew
s.reflectivity *= uDota2SpecularScale;
s.reflectivity *= mask2.r * max( reflFresnel, metalness );
s.fresnel = vec3(0.0,0.0,0.0);
//convert phong exponent to marmoset gloss
float specExp = mask2.a * uDotaSpecularExponent;
s.gloss = exp2( -10.0 * rsqrt(4.0*specExp + 0.001) );
//rim lighting
float rim = saturate( rimFresnel * mask2.g * s.normal.y );
rim = rim - rim*metalness;
s.specularLight += rim * uDota2RimLightScale;
}
Metalness and adv.Metalness
void ReflectivityMetalness( inout FragmentState s )
{
float m = dot( texture2D( tMetalnessMap, s.vertexTexCoord ), uMetalnessSwizzle );
m = uMetalnessScaleBias.x * m + uMetalnessScaleBias.y;
float spec = 0.04;
#ifdef METALNESS_ADVANCED
spec = dot( texture2D( tSpecMap, s.vertexTexCoord ), uSpecSwizzle );
spec = (uSpecCurveAdjust > 0) ? (spec*spec) : spec;
spec = uSpecScaleBias.x * spec + uSpecScaleBias.y;
#endif
s.reflectivity = mix( vec3(spec,spec,spec), s.albedo.xyz, m );
s.albedo.xyz = s.albedo.xyz - m * s.albedo.xyz;
s.fresnel = vec3( 1.0, 1.0, 1.0 );
}
RefractiveIndex
void ReflectivityRefractiveIndex( inout FragmentState s )
{
//IOR of the scene medium
vec3 n1 = vec3( uReflectivityMediumIndex, uReflectivityMediumIndex, uReflectivityMediumIndex );
//IOR of the surface
vec3 mask = texture2D( tReflectivityRefractiveIndex, s.vertexTexCoord ).xyz;
vec3 n2 = mix( vec3(1.0,1.0,1.0), uReflectivitySurfaceIndex, mask );
vec3 k2 = mix( vec3(0.0,0.0,0.0), uReflectivitySurfaceExtinction, mask );
//shout out to AJ Fresnel
s.reflectivity = ((n1-n2)*(n1-n2) + k2*k2) / ((n1+n2)*(n1+n2) + k2*k2);
s.fresnel = vec3(1.0,1.0,1.0);
//energy conservation plz
s.albedo.xyz -= s.albedo.xyz*s.reflectivity;
}
SpecularMap
void ReflectivitySpecularMap( inout FragmentState s )
{
vec4 t = texture2D( tSpecularMap, s.vertexTexCoord );
float swz = dot( t, uSpecularSwizzle );
s.reflectivity = (uSpecularSwizzle.x < 0.0) ? t.rgb : vec3(swz,swz,swz);
s.reflectivity *= uSpecularColor;
s.albedo.xyz = s.albedo.xyz - s.albedo.xyz * uSpecularConserve * s.reflectivity;
s.fresnel = uSpecularFresnel;
}
Reflection
Aniso
void ReflectionAniso( inout FragmentState s )
{
vec3 tangent;
//Direction
tangent = texture2D( tAnisoDirectionMap, s.vertexTexCoord ).xyz;
tangent = uAnisoDirectionMapScale*tangent + uAnisoDirectionMapBias;
//zero if a tangent map is present
tangent += uAnisoDirectionConst;
tangent.xy = tangent.yx;
tangent.x = -tangent.x;
//swizzle tangent and binormal directions
//NOTE: swizzle order is flipped for the viewer
tangent.xy = mix(tangent.xy, tangent.yx, uAnisoDirectionMapSwizzle);
s.generic3.rgb = normalize( tangent );
//OPT: technically tangent could be vec2 and uAnisoSpread is a constant in Toolbag. It could be a map in the future.
}
GGX
void ReflectionGGX( inout FragmentState s )
{
//scale reflectivity a bit to roughly match ggx occlusion terms
s.reflectivity *= 0.4 + 0.6*saturate( s.gloss/0.5 );
//emperical s-curve to match phong as well as we can
float left = 0.87, right = 1.0 - left;
if( s.gloss <= left )
{ s.gloss = s.gloss*s.gloss / left; }
else
{ s.gloss = sqrt( (s.gloss-left)/right )*right + left; }
}
Mirror
void ReflectionMirror( inout FragmentState s )
{
s.gloss = 1.0; //convert gloss value for export
}
BlinnPhong
void BlinnPhongLight( inout FragmentState s, LightParams l )
{
//determine specular exponent from gloss map & settings
float gloss = min( s.gloss, 0.995 );
#ifdef SPECULAR_SECONDARY
gloss = saturate( gloss * uPhongSecondaryGloss );
#endif
float specExp = -10.0 / log2( gloss*0.968 + 0.03 );
specExp *= specExp;
//light params
float phongNormalize = (specExp + 4.0)/(8.0*3.141592);
adjustAreaLightSpecular( l, reflect( -s.vertexEye, s.normal ), phongNormalize );
//blinn-phong term
vec3 H = normalize( s.vertexEye + l.direction );
float phong = phongNormalize * pow( saturate( dot(H, s.normal) ), specExp );
//horizon occlusion
float horizon = 1.0 - saturate( dot( l.direction, s.normal ) );
horizon *= horizon; horizon *= horizon;
phong = phong - phong*horizon;
//spec color
vec3 spec = (l.shadow.rgb * l.attenuation) *
saturate( dot( l.direction, s.normal ) ) *
l.color;
//fresnel
float glossAdjust = gloss*gloss;
vec3 reflectivity = s.reflectivity, fresn = s.fresnel;
#ifdef SPECULAR_SECONDARY
reflectivity *= uPhongSecondaryIntensity;
fresn = uPhongSecondaryFresnel;
#endif
spec *= fresnel( dot( s.vertexEye, s.normal ),
reflectivity,
fresn * glossAdjust );
//add it on
s.specularLight += spec * phong;
}
ReflectionSecondary
void ReflectionAnisotropicLight( inout FragmentState s, LightParams l )
{
#ifdef SPECULAR_SECONDARY
float gloss = saturate( s.gloss * uAnisoSecondaryGloss );
#else
float gloss = s.gloss;
#endif
adjustAreaLightSpecular( l, reflect( -s.vertexEye, s.normal ), 1.0-gloss );
//gloss
//determine specular exponent from gloss map & settings
float specExp = -10.0 / log2( gloss*0.968 + 0.03 );
specExp *= specExp;
float anisoExp = mix( specExp, 16.0, uAnisoSpread );
//blinn-phong
vec3 h = normalize( s.vertexEye + l.direction );
//anisotropic projection
//h is projected onto the B-plane (shared by strand dir and N) to max out the dot-product along the B axis.
vec3 N = s.normal;
vec3 T, B; SampleAnisoTangent( s, T, B );
#ifdef SPECULAR_SECONDARY
N = normalize(N + T * uAnisoSecondaryShift * 0.5);
#endif
//TODO: sqrt in c++, square it everywhere else
h = normalize(h - B*dot(h,B) * sqrt(uAnisoSpread));
float HdotN = saturate( dot( h, N ) );
float illum = pow( HdotN, specExp );
illum *= (specExp + 4.0)/(8.0*3.141592);
illum *= 1.0 - uAnisoSpread * 0.5;
//horizon
float horizon = 1.0 - saturate( dot( l.direction, N ) );
horizon *= horizon; horizon *= horizon;
illum = illum - illum*horizon;
//spec color
vec3 spec = (l.attenuation * illum) * l.color;
//fresnel
float glossAdjust = gloss*gloss;
vec3 reflectivity = s.reflectivity, fresn = s.fresnel;
#ifdef SPECULAR_SECONDARY
reflectivity *= uAnisoSecondaryColor;
fresn = uAnisoSecondaryFresnel;
#endif
spec *= fresnel( dot( s.vertexEye, s.normal ),
reflectivity,
fresn * glossAdjust );
#ifdef SPECULAR_SECONDARY
spec *= uAnisoSecondaryColor;
#endif
//add it on
s.specularLight += spec * l.shadow.rgb;
}
ggx
void ReflectionGGXLight( inout FragmentState s, LightParams l )
{
//roughness
float roughness = 1.0 - s.gloss;
#ifdef SPECULAR_SECONDARY
roughness = saturate(roughness - roughness*uGGXSecondaryGloss);
#endif
float a = max( roughness * roughness, 2e-3 );
float a2 = a * a;
//light params
adjustAreaLightSpecular( l, reflect( -s.vertexEye, s.normal ), rcp(3.141592 * a2) );
//various dot products
vec3 H = normalize(l.direction + s.vertexEye);
float NdotH = saturate(dot(s.normal,H));
float VdotN = saturate(dot(s.vertexEye,s.normal));
float LdotN = saturate(dot(l.direction,s.normal));
float VdotH = saturate(dot(s.vertexEye,H));
//horizon
float atten = l.attenuation;
float horizon = 1.0 - LdotN;
horizon *= horizon; horizon *= horizon;
atten = atten - atten * horizon;
//incident light
vec3 spec = l.color * l.shadow.rgb * (atten * LdotN);
//microfacet distribution
float d = ( NdotH * a2 - NdotH ) * NdotH + 1.0;
d *= d;
float D = a2 / (3.141593 * d);
//geometric / visibility
float k = a * 0.5;
float G_SmithL = LdotN * (1.0 - k) + k;
float G_SmithV = VdotN * (1.0 - k) + k;
float G = 0.25 / ( G_SmithL * G_SmithV );
//fresnel
vec3 reflectivity = s.reflectivity, fresn = s.fresnel;
#ifdef SPECULAR_SECONDARY
reflectivity *= uGGXSecondaryIntensity;
fresn = uGGXSecondaryFresnel;
#endif
vec3 F = reflectivity + (fresn - fresn*reflectivity) * exp2( (-5.55473 * VdotH - 6.98316) * VdotH );
//final
s.specularLight += (D * G) * (F * spec);
}
Mirror
void MirrorLight( inout FragmentState s, LightParams l )
{
//boolean ray trace against light shape
vec3 r = reflect( -s.vertexEye, s.normal );
bool hit = adjustAreaLightSpecular( l, r, 1.0 );
hit = hit && dot(r,l.direction) > 0.0;
vec3 spec = (hit ? l.attenuation : 0.0) * l.color;
//fresnel
vec3 reflectivity = s.reflectivity, fresn = s.fresnel;
#ifdef SPECULAR_SECONDARY
reflectivity *= uMirrorSecondaryIntensity;
fresn = uMirrorSecondaryFresnel;
#endif
spec *= fresnel( dot( s.vertexEye, s.normal ),
reflectivity,
fresn );
//add it on
s.specularLight += spec * l.shadow.rgb;
}
BlinnPhong
void BlinnPhongLight( inout FragmentState s, LightParams l )
{
//determine specular exponent from gloss map & settings
float gloss = min( s.gloss, 0.995 );
#ifdef SPECULAR_SECONDARY
gloss = saturate( gloss * uPhongSecondaryGloss );
#endif
float specExp = -10.0 / log2( gloss*0.968 + 0.03 );
specExp *= specExp;
//light params
float phongNormalize = (specExp + 4.0)/(8.0*3.141592);
adjustAreaLightSpecular( l, reflect( -s.vertexEye, s.normal ), phongNormalize );
//blinn-phong term
vec3 H = normalize( s.vertexEye + l.direction );
float phong = phongNormalize * pow( saturate( dot(H, s.normal) ), specExp );
//horizon occlusion
float horizon = 1.0 - saturate( dot( l.direction, s.normal ) );
horizon *= horizon; horizon *= horizon;
phong = phong - phong*horizon;
//spec color
vec3 spec = (l.shadow.rgb * l.attenuation) *
saturate( dot( l.direction, s.normal ) ) *
l.color;
//fresnel
float glossAdjust = gloss*gloss;
vec3 reflectivity = s.reflectivity, fresn = s.fresnel;
#ifdef SPECULAR_SECONDARY
reflectivity *= uPhongSecondaryIntensity;
fresn = uPhongSecondaryFresnel;
#endif
spec *= fresnel( dot( s.vertexEye, s.normal ),
reflectivity,
fresn * glossAdjust );
//add it on
s.specularLight += spec * phong;
}
OcclusionMap
void OcclusionMap( inout FragmentState s )
{
float diff = 1.0, spec = 1.0;
#ifdef AMBIENT_OCCLUSION
vec2 tc = mix( s.vertexTexCoord, s.vertexTexCoordSecondary, uAmbientOcclusionUseSecondaryUV );
float ao = dot( texture2D( tAmbientOcclusionTexture, tc ), uAmbientOcclusionSwizzle );
ao *= dot( s.vertexColor, uAmbientOcclusionVertexSwizzle ) + uAmbientOcclusionVertexEnable;
ao = ao * uAmbientOcclusionStrength.x + uAmbientOcclusionStrength.y;
#ifdef OCCLUSION_EXPORT
//output ao instead of applying it
s.output2.a = sqrt(ao);
#else
diff = ao;
#endif
#endif
//cavity mapping
float cav = dot( texture2D( tCavityTexture, s.vertexTexCoord ), uCavitySwizzle );
diff *= cav * uCavityStrength.x + uCavityStrength.y;
spec *= cav * uCavityStrength.z + uCavityStrength.w;
#ifdef OCCLUSION_EXPORT
//export; apply cavity to surface instead
s.albedo.xyz *= diff;
s.reflectivity *= spec;
#else
s.diffuseLight *= diff;
s.specularLight *= spec;
#endif
}
Emissive
ShadowCatcherEmissive
void DiffusionShadowCatcherEmissive( inout FragmentState s )
{
//shadow ratio
float eps = 1.0e-4;
float ao = sampleOcclusionMask( s.screenTexCoord );
s.diffuseLight = (s.diffuseLight * ao + eps) / (s.generic0.rgb + eps);
//edge fade
vec2 fadeCoords = s.vertexTexCoord * 2 - 1;
float edgeFade = saturate( dot(fadeCoords,fadeCoords) ) * uShadowCatcherParams.z;
s.diffuseLight = mix( s.diffuseLight, vec3(1.0,1.0,1.0), edgeFade );
//alpha fade
s.diffuseLight = mix( vec3(1.0,1.0,1.0), s.diffuseLight, uShadowCatcherParams.y * dot( texture2D( tShadowCatcherAlphaMap, s.vertexTexCoord ), uShadowCatcherAlphaMapSwizzle ) );
}
FluorescentImage
void EmissiveFluorescentImage( inout FragmentState s )
{
float e = uFluorescentSphere[0];
e += uFluorescentSphere[1] * s.normal.y;
e += uFluorescentSphere[2] * s.normal.z;
e += uFluorescentSphere[3] * s.normal.x;
vec3 swz = s.normal.yyz * s.normal.xzx;
e += uFluorescentSphere[4] * swz.x;
e += uFluorescentSphere[5] * swz.y;
e += uFluorescentSphere[7] * swz.z;
vec3 sqr = s.normal * s.normal;
e += uFluorescentSphere[6] * ( 3.0*sqr.z - 1.0 );
e += uFluorescentSphere[8] * ( sqr.x - sqr.y );
e *= sampleOcclusionMask( s.screenTexCoord );
s.emissiveLight += uFluorescentColor * texture2D(uFluorescentMap,s.vertexTexCoord).xyz * e;
}
Heat
void EmssiveHeat( inout FragmentState s )
{
float temp = mix( uEmissiveHeatRange.x, uEmissiveHeatRange.y, texture2D( tEmissiveHeatMap, s.vertexTexCoord ).x );
float mireds = 1000.0 / temp; //actually mireds divided by 1000
float intensity = saturate( temp/ 10000.0 );
intensity *= intensity;
intensity *= uEmissiveHeatRange.z;
s.emissiveLight += intensity * texture2D( tEmissiveHeatSpectrum, vec2(mireds,0.0) ).xyz;
}
void EmssiveMap( inout FragmentState s )
{
vec2 tc = mix( s.vertexTexCoord, s.vertexTexCoordSecondary, uEmissionUseSecondaryUV );
vec3 emissiveMap = texture2D( tEmissiveMap, tc ).xyz;
s.emissiveLight += uEmission + emissiveMap * uEmissiveColor;
}
Transparency
void TransparencyAlphaTest( inout FragmentState s )
{
AlphaBase(s);
HINT_FLATTEN
if( s.albedo.a < uAlphaTestValue )
{ discard; }
}
void TransparencyBlend( inout FragmentState s )
{
AlphaBase(s);
float diffuseScale = uDiffuseScale * s.albedo.a;
s.diffuseLight *= diffuseScale;
s.albedo.xyz *= diffuseScale;
s.specularLight *= s.albedo.a;
s.reflectivity.xyz *= s.albedo.a;
s.emissiveLight *= s.albedo.a;
}
void TransparencyDither( inout FragmentState s )
{
AlphaBase(s);
vec2 tc = s.screenTexCoord * uAlphaDitherScaleBias.xy + uAlphaDitherScaleBias.zw;
tc += floor( s.vertexTexCoord * uLayerFloorScale.x ) * uLayerFloorScale.y;
float noise = texture2D( tDitherPattern, tc ).x;
HINT_FLATTEN
if( s.albedo.a < noise )
{ discard; }
}